package com.ciei.dpagm.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ciei.dpagm.common.enums.*;
import com.ciei.dpagm.common.service.BaseService;
import com.ciei.dpagm.entity.Right;
import com.ciei.dpagm.entity.Role;
import com.ciei.dpagm.mapper.RightMapper;
import com.ciei.dpagm.util.APP;
import com.ciei.dpagm.util.JsonUtil;
import com.ciei.dpagm.util.TreeNode;
import com.ciei.dpagm.util.TreeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author fangnuozhou
 * @date 2020/09/01
 */
@Service
public class RightService  extends BaseService<RightMapper, Right> {

	@Autowired
	RoleRightService roleRightService;
	@Autowired
	RoleService roleService;
	@Autowired
	UsersService usersService;


	/**
	 * 获取权限树的ID和标题
	 *
	 * @param filterDisable true表示过滤掉已禁用的权限
	 * @param onlyMenu      true表示只返回菜单类型
	 * @return
	 */
	public List<TreeNode> loadTree(boolean filterDisable, boolean onlyMenu) {
		LambdaQueryWrapper<Right> queryWrapper = new LambdaQueryWrapper<>();
		if (filterDisable) {
			queryWrapper.eq(Right::getDisable, RightDisableEnum.ENABLE.getValue());
		}
		if (onlyMenu) {
			queryWrapper.eq(Right::getRightType, RightTypeEnum.MENU.getTypeId());
		}
		queryWrapper.orderByAsc(Right::getOrderId);
		List<Right> rightList = list(queryWrapper);
		return TreeUtils.buildTree(rightList, Right::getRightId, Right::getParentId, Right::getRightName, Right::getOrderId);
	}

	/**
	 * 获取路由菜单
	 * @param roleIds	角色ID列表
	 * @return			菜单树形数据
	 */
	public JSONArray loadRightMenuTreeByRoleId(List<Integer> roleIds) {
		Set<Integer> rightIds = roleRightService.loadRightIdsByRoleId(roleIds, false);
		List<Right> rightMenuList = findMenu(rightIds, true);
		return buildRightMenuTree(rightMenuList, 0);
	}

	/**
	 * 构建菜单树形结构数据
	 *
	 * @param allRightMenuList 菜单列表
	 * @param pid              父菜单ID
	 * @return
	 */
	public JSONArray buildRightMenuTree(List<Right> allRightMenuList, Integer pid) {
		JSONArray jsonArray = new JSONArray();
		if (allRightMenuList == null || allRightMenuList.isEmpty()) {
			return jsonArray;
		}
		List<Right> rightList = allRightMenuList.stream()
				.filter(right -> pid.equals(right.getParentId()))
				.sorted(Comparator.comparingInt(Right::getOrderId))
				.collect(Collectors.toList());
		for (Right right : rightList) {
			JSONObject jsonObject = new JSONObject();
			jsonObject.fluentPut("path", right.getPath())
					.fluentPut("component", right.getComponent())
					.fluentPut("name", right.getRouterName())
					.fluentPut("alwaysShow", RightAlwaysShowEnum.getBooleanByValue(right.getAlwaysShow()))
					.fluentPut("hidden", RightHiddenEnum.getBooleanByValue(right.getHidden()));
			if (StringUtils.isNotBlank(right.getRedirect())) {
				jsonObject.put("redirect", right.getRedirect());
			}
			JSONObject metaObject = new JSONObject();
			metaObject.put("title", right.getRightName());
			if (StringUtils.isNotBlank(right.getIcon())) {
				metaObject.put("icon", right.getIcon());
			}
			if (right.getNoCache() != null) {
				metaObject.put("noCache", RightNoCacheEnum.getBooleanByValue(right.getNoCache()));
			}
			metaObject.put("image", right.getImage());
			metaObject.put("affix", RightAffixEnum.getBooleanByValue(right.getAffix()));
			jsonObject.put("meta", metaObject);
			JSONArray children = buildRightMenuTree(allRightMenuList, right.getRightId());
			if (!children.isEmpty()) {
				jsonObject.put("children", children);
			}
			jsonArray.add(jsonObject);
		}
		return jsonArray;
	}

	/**
	 * 获取用户拥有的权限编码列表
	 * （过滤已停用的角色和权限）
	 *
	 * @param userId 用户ID
	 * @return 权限编码列表
	 */
	public Set<String> getRightCodeSet(Integer userId) {
		LambdaQueryWrapper<Right> wrapper = new LambdaQueryWrapper<>();
		wrapper.select(Right::getRightCode);
		boolean isAdmin = usersService.isAdmin(userId);
		if (!isAdmin) {
			List<Role> enableRoleList = roleService.getEnableRoleList(userId);
			Set<Integer> roleIdSet = enableRoleList.stream().map(Role::getRoleId).collect(Collectors.toSet());
			Set<Integer> rightIdSet = roleRightService.getRightIdSet(roleIdSet);
			wrapper.eq(Right::getDisable, RightDisableEnum.ENABLE.getValue())
					.in(Right::getRightId, rightIdSet);
		}
		List<Right> rightList = list(wrapper);
		return rightList.stream()
				.filter(right -> right != null && StringUtils.isNotBlank(right.getRightCode()))
				.map(Right::getRightCode).collect(Collectors.toSet());
	}

	/**
	 * 查找菜单
	 * (停用的不返回，隐藏的要返回)
	 *
	 * @param rightIds 权限ID列表
	 * @param filterDisable true 表示过滤掉已禁用的菜单
	 * @return 菜单列表
	 */
	public List<Right> findMenu(Collection<Integer> rightIds, boolean filterDisable) {
		LambdaQueryWrapper<Right> wrapper = Wrappers.lambdaQuery();
		wrapper.eq(Right::getRightType, RightTypeEnum.MENU.getTypeId());
		if (rightIds != null && !rightIds.isEmpty()) {
			wrapper.in(Right::getRightId, rightIds);
		}
		if (filterDisable) {
			wrapper.eq(Right::getDisable, RightDisableEnum.ENABLE.getValue());
		}
		return list(wrapper);
	}

	/**
	 * 查找所有菜单
	 * @return
	 */
	public List<Right> findAllMenu() {
		return findMenu(null, false);
	}

	/**
	 * 查找按钮权限
	 *
	 * @param rightIds      权限ID集合
	 * @param filterDisable true 表示过滤掉已禁用和隐藏的按钮
	 * @return 按钮集合
	 */
	public List<Right> findPermission(Collection<Integer> rightIds, boolean filterDisable) {
		LambdaQueryWrapper<Right> wrapper = Wrappers.lambdaQuery();
		wrapper.eq(Right::getRightType, RightTypeEnum.PERMISSION.getTypeId());
		if (rightIds != null && !rightIds.isEmpty()) {
			wrapper.in(Right::getRightId, rightIds);
		}
		if (filterDisable) {
			wrapper.eq(Right::getDisable, RightDisableEnum.ENABLE.getValue());
			wrapper.eq(Right::getHidden, RightHiddenEnum.VISIBLE.getValue());
		}
		return list(wrapper);
	}

	/**
	 * 查找按钮权限
	 *
	 * @param roleIds 角色ID集合
	 * @return 按钮权限集合
	 */
	public JSONArray findPermission(Collection<Integer> roleIds) {
		Set<Integer> rightIds = roleRightService.getRightIdSet(roleIds);
		List<Right> permissionList = findPermission(rightIds, true);
		return JSON.parseArray(JsonUtil.toJsonStringIncludes(permissionList, new String[]{"rightId", "parentId", "rightName", "rightCode"}));
	}

	/**
	 * 查找所有按钮权限
	 *
	 * @return 按钮权限集合
	 */
	public JSONArray findAllPermission() {
		List<Right> permissionList = findPermission(null, false);
		return JSON.parseArray(JsonUtil.toJsonStringIncludes(permissionList, new String[]{"rightId", "parentId", "rightName", "rightCode"}));
	}

	/**
	 * 加载权限树形表格
	 *
	 * @return 权限树形表格数据
	 */
	public JSONObject loadRightGird(JSONObject params) {
		// 父ID
		Integer parentRightId = params.getInteger("parentRightId");
		// 权限名称（标题）
		String rightName = params.getString("rightName");
		// 1：禁用，0：不禁用
		Integer disable = params.getInteger("disable");

		// 待构建的权限集
		List<Right> allRightList = list();
		// 已完成构建树的权限集
		List<Right> resultList = buildTree(allRightList, parentRightId);

		Set<Right> allRightSet = new HashSet<>();
		resultList.forEach(r -> getAllChildrenSet(r, allRightSet));

		allRightList = new ArrayList<>(allRightSet);

		if (StringUtils.isNotBlank(rightName)) {
			allRightList = allRightList.stream().filter(right -> right.getRightName().contains(rightName)).collect(Collectors.toList());
		}
		if (disable != null) {
			allRightList = allRightList.stream().filter(right -> disable.equals(right.getDisable())).collect(Collectors.toList());
		}
		// 需二次构建树
		resultList = buildTree(allRightList, null);

		return JsonUtil.getSuccess("查询成功").fluentPut(APP.DATA, JSON.parseArray(JsonUtil.toJsonStringExcludes(resultList, new String[]{"isDelete"})));
	}

	/**
	 * 构建权限树
	 *
	 * @param rightList children为空的权限集合
	 * @return 携带children的权限集合
	 */
	public void buildTree(List<Right> rightList) {
		// 子节点集合：按父ID分组
		Map<Integer, List<Right>> childrenMap = rightList.stream()
				.filter(right -> right.getParentId() != null && right.getParentId() != 0)
				.sorted(Comparator.comparingInt(Right::getOrderId))
				.collect(Collectors.groupingBy(Right::getParentId));
		// 父子关联
		rightList.forEach(right -> {
			List<Right> childrenList = childrenMap.get(right.getRightId());
			if (childrenList != null) {
				childrenList.sort(Comparator.comparingInt(Right::getOrderId));
				right.setChildren(childrenList);
			}
		});
	}

	/**
	 * 构建权限树
	 *
	 * @param rightList     children为空的权限集合
	 * @param parentRightId 指定父ID
	 * @return 携带children的父节点
	 */
	public List<Right> buildTree(List<Right> rightList, Integer parentRightId) {
		List<Right> resultList = new ArrayList<>();
		buildTree(rightList);
		if (parentRightId == null) {
			// 筛选出顶层
			for (Right right : rightList) {
				boolean isTopLevel = rightList.stream().noneMatch(r -> r.getRightId().equals(right.getParentId()));
				if (isTopLevel) {
					resultList.add(right);
				}
			}
		} else {
			// 筛选出指定层级
			resultList = rightList.stream().filter(right -> parentRightId.equals(right.getRightId())).collect(Collectors.toList());
		}
		resultList.sort(Comparator.comparingInt(Right::getOrderId));
		return resultList;
	}

	/**
	 * 构建权限树
	 *
	 * @param parentRightId 指定父ID
	 * @return 携带children的父节点
	 */
	public Right buildTree(Integer parentRightId) {
		List<Right> allRightList = list();
		return buildTree(allRightList, parentRightId).stream().findFirst().orElse(null);
	}

	/**
	 * 获取本身及子孙权限集合
	 *
	 * @param parentRightId 指定父ID
	 * @return 本身及子孙权限集合
	 */
	public Set<Right> getAllChildrenSet(Integer parentRightId) {
		Set<Right> allChildrenSet = new HashSet<>();
		Right treeRight = buildTree(parentRightId);
		if (treeRight != null) {
			getAllChildrenSet(treeRight, allChildrenSet);
		}
		return allChildrenSet;
	}

	/**
	 * 递归获取本身及子孙权限集合
	 *
	 * @param rootTreeRight  （带children的）起始节点
	 * @param allChildrenSet 本身及子孙权限集合
	 */
	public void getAllChildrenSet(Right rootTreeRight, Set<Right> allChildrenSet) {
		if (rootTreeRight != null) {
			allChildrenSet.add(rootTreeRight);
			List<Right> childrenList = rootTreeRight.getChildren();
			if (childrenList != null && childrenList.size() > 0) {
				for (Right children : childrenList) {
					getAllChildrenSet(children, allChildrenSet);
				}
			}
		}
	}

	/**
	 * 构建权限表格树形结构
	 * @param allRightList 所有权限
	 * @param parentRightId 父ID
	 * @return
	 */
	public JSONArray buildTableTree(List<Right> allRightList,Integer parentRightId) {
		JSONArray jsonArray = new JSONArray();
		if (allRightList == null || allRightList.isEmpty()) {
			return jsonArray;
		}
		List<Right> sonRightList = allRightList.stream()
				.filter(right -> parentRightId.equals(right.getParentId()))
				.sorted(Comparator.comparingInt(Right::getOrderId))
				.collect(Collectors.toList());

		for (Right right : sonRightList) {
			JSONObject jsonObject = JSON.parseObject(JsonUtil.toJsonStringExcludes(right, new String[]{"isDelete"}));
			JSONArray children = buildTableTree(allRightList, right.getRightId());
			if (!children.isEmpty()) {
				jsonObject.put("children", children);
			}
			jsonArray.add(jsonObject);
		}
		return jsonArray;
	}

	/**
	 * 停用/启用权限
	 *
	 * @param rightId 权限ID
	 * @param disable 0启用、1停用
	 * @return
	 */
	@Transactional
	public JSONObject disableRight(Integer rightId, Integer disable) {
		if (rightId == null || disable == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "参数不能为空");
		}
		Right right = findById(rightId);
		if (right == null) {
			return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND, "找不到该权限");
		}
		disable(rightId, disable);
		return JsonUtil.getSuccess("操作成功");
	}

	/**
	 * 停用/启用权限
	 *
	 * @param rightId 权限ID
	 * @param disable 0启用、1停用
	 */
	@Transactional(rollbackFor = Exception.class)
	public void disable(Integer rightId, Integer disable) {
		Set<Right> rightSet = getAllChildrenSet(rightId);
		if (!rightSet.isEmpty()) {
			rightSet.forEach(r -> {
				LambdaUpdateWrapper<Right> updateWrapper = Wrappers.lambdaUpdate();
				updateWrapper.eq(Right::getRightId, r.getRightId());
				updateWrapper.set(Right::getDisable, disable);
				update(updateWrapper);
			});
		}
	}

	/**
	 * 删除权限
	 *
	 * @param rightId 权限ID
	 * @return
	 */
	@Transactional
	public JSONObject delete(Integer rightId) {
		if (rightId == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "参数不能为空");
		}
		Right right = findById(rightId);
		if (right == null) {
			return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND, "找不到该权限");
		}
		Set<Right> rightSet = getAllChildrenSet(rightId);
		if (rightSet.isEmpty()) {
			return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND, "找不到该权限");
		}
		Set<Integer> rightIdSet = rightSet.stream().map(Right::getRightId).collect(Collectors.toSet());
		boolean success = removeByIds(rightIdSet);
		if (success) {
			roleRightService.delete(rightIdSet);
			return JsonUtil.getSuccess("删除成功");
		}
		return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND, "操作失败");
	}

	/**
	 * 新增或修改权限
	 *
	 * @param rightJson 权限数据
	 * @return
	 */
	@Transactional(rollbackFor = Exception.class)
	public JSONObject saveOrUpdate(JSONObject rightJson) {
		Right right = JSON.parseObject(rightJson.toJSONString(), Right.class);
		if (right == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "无效参数");
		}
		if (StringUtils.isBlank(right.getRightName())) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "权限名称不能为空");
		}
		if (right.getOrderId() == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "排序号不能为空");
		}
		if (right.getHidden() == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "hidden字段不能为空");
		}
		if (right.getDisable() == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "disable字段不能为空");
		}
		if (right.getParentId() == null) {
			right.setParentId(0);
		}
		String rightCode = right.getRightCode();
		RightTypeEnum rightTypeEnum = RightTypeEnum.getEnumByValue(right.getRightType());
		if (rightTypeEnum == null) {
			return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "权限类型错误");
		}
		switch (rightTypeEnum) {
			case MENU:
				if (StringUtils.isBlank(right.getRouterName())) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "路由名称不能为空");
				}
				if (StringUtils.isBlank(right.getPath())) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "路由地址不能为空");
				}
				if (StringUtils.isBlank(right.getComponent())) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "组件路径不能为空");
				}
				if (right.getNoCache() == null) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "noCache字段不能为空");
				}
				if (right.getAlwaysShow() == null) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "alwaysShow字段不能为空");
				}
				if (right.getAffix() == null) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "affix字段不能为空");
				}
				break;
			case PERMISSION:
				if (StringUtils.isBlank(rightCode)) {
					return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "权限标识不能为空");
				}
				break;
			default:
				return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "权限类型错误");
		}
        Integer rightId = right.getRightId();
		if (rightId != null) {
			Set<Right> allChildrenSet = getAllChildrenSet(rightId);
			Set<Integer> allChildrenRightIdSet = allChildrenSet.stream().map(Right::getRightId).collect(Collectors.toSet());
			if (allChildrenRightIdSet.contains(right.getParentId())) {
				return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "上级菜单不能选择自己本身或下属菜单");
			}
		}

        // 检查rightCode是否唯一
        boolean rightCodeIsUnique;
		if (StringUtils.isNotBlank(rightCode)) {
			if (rightId == null) {
				rightCodeIsUnique = rightCodeIsUnique(rightCode, null);
			} else {
				rightCodeIsUnique = rightCodeIsUnique(rightCode, rightId);
			}
			if (!rightCodeIsUnique) {
				return JsonUtil.getFail(ErrorCodeEnum.INVALID_PARAM, "权限标识已存在");
			}
		}
		boolean success = saveOrUpdate(right);
		if (success) {
			// 更新设置禁用时，子孙权限也要禁用
			if (rightId != null) {
				Right oldRight = findById(rightId);
				if (oldRight == null) {
					return JsonUtil.getFail(ErrorCodeEnum.DATA_NOT_FOUND, "找不到该权限");
				}
				disable(rightId, right.getDisable());
			}
			return JsonUtil.getSuccess("操作成功").fluentPut(APP.DATA, right);
		} else {
			return JsonUtil.getFail(ErrorCodeEnum.DATA_OPERATION_FAIL, "操作失败").fluentPut(APP.DATA, right);
		}
	}

	/**
	 * 检查rightCode是否唯一
	 * @param checkRightCode 要检查的rightCode
	 * @param exceptRightId 排除rightId,一般是本身
	 * @return true表示参数“checkRightCode”是唯一的
	 */
	public boolean rightCodeIsUnique(String checkRightCode, Integer exceptRightId) {
		if (StringUtils.isBlank(checkRightCode)) {
			return false;
		}
		LambdaQueryWrapper<Right> wrapper = Wrappers.lambdaQuery();
		wrapper.eq(Right::getRightCode, checkRightCode);
		if (exceptRightId != null) {
			wrapper.ne(Right::getRightId, exceptRightId);
		}
		return count(wrapper) == 0;
	}

	/**
	 * 获取注解@RequiresPermissions上的权限标识符
	 *
	 * @return
	 */
	public JSONObject loadRightCodeOptions() {
		Set<String> rightCodeSet = new TreeSet<>();
		String packagePath = "com.ciei.dpagm.controller";
		ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(true);
		Set<BeanDefinition> components = provider.findCandidateComponents(packagePath);
		try {
			for (BeanDefinition component : components) {
				Class<?> clazz = Class.forName(component.getBeanClassName());
				Method[] declaredMethods = clazz.getDeclaredMethods();
				if (declaredMethods.length > 0) {
					for (Method method : declaredMethods) {
						RequiresPermissions requiresPermissionsAnnotation = method.getAnnotation(RequiresPermissions.class);
						if (requiresPermissionsAnnotation != null) {
							String[] permissionsValues = requiresPermissionsAnnotation.value();
							if (permissionsValues.length > 0) {
								rightCodeSet.addAll(Arrays.asList(permissionsValues));
							}
						}
					}
				}
			}
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		JSONArray permissionsArray = new JSONArray();
		for (String rightCode : rightCodeSet) {
			JSONObject itemJson = new JSONObject();
			itemJson.fluentPut("label", rightCode)
					.fluentPut("value", rightCode);
			permissionsArray.add(itemJson);
		}
		JSONObject optionsJson = new JSONObject();
		optionsJson.put("options", permissionsArray);
		return JsonUtil.getSuccess("获取成功").fluentPut(APP.DATA, optionsJson);
	}
}
